package ml;
import java.util.Date;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;


/**
 * These workers take care of calculating the magnification map corresponding
 * a region of the shooting plane.
 * @author pgomez
 *
 */
public class Deflector extends Thread {

    private static Logger _log = Logger.getLogger(Deflector.class);
    static{
	BasicConfigurator.configure();
    }
    private static String NEW_LINE = System.getProperty("line.separator");
    private StarBag _starBag;
    private final double _startX;
    private final double _startY;
    private final double _endX;
    private final double _endY;

    private final double _stepX;
    private final double _stepY;
    private final double _sizeX;
    private final double _sizeY;
    
    private double[][] _magnificationMap;
    private double[][] _shootingMap;
    private final int _nPixMap;
    private final double _pixSize;
    private long _lostFlux;
    private long _totalFlux;
    private double _x;
    private double _y;
    private long _initialTime;
    private final double _sigma;
    private final double _shear;


    public Deflector(double[][] magnificationMap, double[][] shootingMap, StarBag starBag, 
	    double startX, double endX, double stepX,
	    double startY, double endY, double stepY, 
	    double sizeX, double sizeY,
	    int nPixMap, double pixSize,double sigma, double shear){
	_starBag = starBag;
	_startX = startX;
	_startY = startY;
	_endX = endX;
	_endY = endY;
	_stepX = stepX;
	_stepY = stepY;
	_nPixMap = nPixMap;
	_pixSize = pixSize;
	_magnificationMap = magnificationMap;
	_shootingMap = shootingMap;
	_totalFlux = 0;
	_lostFlux = 0;
	_sigma = sigma;
	_shear = shear;
	_sizeX = sizeX;
	_sizeY = sizeY;
    }

    @Override
    public void run() {
	double[] deflected;
	int pixX,pixY;
	int pixXs,pixYs;
	_initialTime = System.currentTimeMillis();
	
	int shootXPix = _shootingMap[0].length;
	int shootYPix = _shootingMap.length;
	System.out.println("=====================================");
	System.out.println("start y: " + _startY);
	System.out.println("end y: " + _endY);
	System.out.println("start x: " + _startX);
	System.out.println("end x: " + _endX);
	System.out.println("shootXPix: " + shootXPix);
	System.out.println("shootYPix: " + shootYPix);
	System.out.println("=====================================");
	for (_y=_startY + _stepY/2; _y <_endY; _y+= _stepY){
	    for (_x=_startX+ _stepX/2; _x <_endX; _x+= _stepX){
		deflected = getDeflected(_x, _y);
		pixX = (int)Math.round(_nPixMap/2 +(deflected[0]/_pixSize));
		pixY = (int)Math.round(_nPixMap/2 +(deflected[1]/_pixSize));
		pixXs = (int)Math.floor(shootXPix *(_x+_sizeX/2)/_sizeX);
		pixYs = (int)Math.floor(shootYPix *(_y+_sizeY/2)/_sizeY);
		if (pixX <0 || pixX > _nPixMap-1 || pixY <0 || pixY> _nPixMap-1){
		    _lostFlux++;
		} else{
		    _magnificationMap[pixY][pixX]++;
		    _shootingMap[pixYs][pixXs]++;
		}
		_totalFlux++;
	    }
	}
    }

    public long totalFlux(){
	return _totalFlux;
    }

    public long lostFlux(){
	return _lostFlux;
    }

    private double getProgress(){
	double xCompletion = (_x -_startX)/(_endX-_startX);
	return ((_y+_stepY*xCompletion) -_startY)/(_endY-_startY);
    }
    private double getElapsedTime(){
	return (double)(System.currentTimeMillis() - _initialTime)/1000;
    }

    private double [] getDeflected(double xRay, double yRay){
	double [] result = new double[]{xRay, yRay};

	//First the stars contribution.
	for (int i =0;i<_starBag.size();i++){
	    double dx = xRay -_starBag.getX(i);
	    double dy = yRay -_starBag.getY(i);

	    double sep = dx*dx + dy*dy;
	    result[0] -= dx/sep;
	    result[1] -= dy/sep;
	}

	//The distributed mass and the shear
	result[0] -= (_sigma - _shear) * xRay;
	result[1] -= (_sigma + _shear) * yRay;;

	return result;
    }

    @Override
    public String toString() {
	StringBuffer result = new StringBuffer();
	String threadName = Thread.currentThread().getName();
	double elapsed = getElapsedTime();
	double progress = getProgress();
	double remaining = elapsed*(1.0-progress)/progress;
	Date finishDate = new Date(System.currentTimeMillis() + (int)remaining*1000);
	result.append("============================================" + NEW_LINE);
	result.append("Thread:" + threadName + NEW_LINE);
	result.append("Is alive:" + isAlive() + NEW_LINE);

	result.append("Start X: " + _startX+ NEW_LINE);
	result.append("End X: " + _endX+ NEW_LINE);
	result.append("Step X: " + _stepX+ NEW_LINE);
	result.append("Start Y: " + _startY+ NEW_LINE);
	result.append("End Y: " + _endY+ NEW_LINE);
	result.append("Step Y: " + _stepY+ NEW_LINE);
	result.append("N pix map : " + _nPixMap+ NEW_LINE);
	result.append("Pixel size : " + _pixSize+ NEW_LINE);
	result.append("Current X : " + _x+ NEW_LINE);
	result.append("Current Y : " + _y+ NEW_LINE);
	result.append("Progress " + getProgress()+ NEW_LINE);
	result.append("Elapsed time:" + Utils.toHHmmss(elapsed) + NEW_LINE);
	result.append("Remaining time:" + Utils.toHHmmss(remaining) + NEW_LINE);
	result.append("Finishing around:" + finishDate.toString() + NEW_LINE);
	result.append("============================================" + NEW_LINE);

	return result.toString();
    }
    public double[][] getMap() {
	return _magnificationMap;
    }
}
